home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 039a / mawk.zip / BI_FUNCT.C < prev    next >
C/C++ Source or Header  |  1991-04-17  |  18KB  |  782 lines

  1.  
  2. /********************************************
  3. bi_funct.c
  4. copyright 1991, Michael D. Brennan
  5.  
  6. This is a source file for mawk, an implementation of
  7. the Awk programming language as defined in
  8. Aho, Kernighan and Weinberger, The AWK Programming Language,
  9. Addison-Wesley, 1988.
  10.  
  11. See the accompaning file, LIMITATIONS, for restrictions
  12. regarding modification and redistribution of this
  13. program in source or binary form.
  14. ********************************************/
  15.  
  16.  
  17. /* $Log:    bi_funct.c,v $
  18.  * Revision 2.3  91/04/17  06:34:00  brennan
  19.  * index("","") should be 1 not 0 for consistency with match("",//)
  20.  * 
  21.  * Revision 2.2  91/04/09  12:38:42  brennan
  22.  * added static to funct decls to satisfy STARDENT compiler
  23.  * 
  24.  * Revision 2.1  91/04/08  08:22:17  brennan
  25.  * VERSION 0.97
  26.  * 
  27. */
  28.  
  29.  
  30. #include "mawk.h"
  31. #include "bi_funct.h"
  32. #include "bi_vars.h"
  33. #include "memory.h"
  34. #include "init.h"
  35. #include "files.h"
  36. #include "fin.h"
  37. #include "field.h"
  38. #include "regexp.h"
  39. #include "repl.h"
  40. #include <math.h>
  41.  
  42. #ifndef  BSD43
  43. void PROTO( srand48, (long) ) ;
  44. double PROTO( drand48, (void) ) ;
  45. #endif
  46.  
  47. /* statics */
  48. static STRING *PROTO(gsub, (PTR, CELL *, char *, int) ) ;
  49. static void  PROTO( fplib_err, (char *, double, char *) ) ;
  50.  
  51.  
  52. /* global for the disassembler */
  53. BI_REC  bi_funct[] = { /* info to load builtins */
  54.  
  55. "index" , bi_index , 2, 2 ,
  56. "substr" , bi_substr, 2, 3,
  57. "sprintf" , bi_sprintf, 1, 255,
  58. "sin", bi_sin , 1, 1 ,
  59. "cos", bi_cos , 1, 1 ,
  60. "atan2", bi_atan2, 2,2,
  61. "exp", bi_exp, 1, 1,
  62. "log", bi_log , 1, 1 ,
  63. "int", bi_int, 1, 1,
  64. "sqrt", bi_sqrt, 1, 1,
  65. "rand" , bi_rand, 0, 0,
  66. "srand", bi_srand, 0, 1,
  67. "close", bi_close, 1, 1,
  68. "system", bi_system, 1, 1,
  69.  
  70. #if  DOS   /* this might go away, when pipes and system are added
  71.           for DOS  */
  72. "errmsg", bi_errmsg, 1, 1,
  73. #endif
  74.  
  75. (char *) 0, (PF_CP) 0, 0, 0 } ;
  76.  
  77.  
  78.  
  79. void bi_funct_init()
  80. { register BI_REC *p = bi_funct ;
  81.   register SYMTAB *stp ;
  82.  
  83.   while ( p->name )
  84.   { stp = insert( p->name ) ;
  85.     stp->type = ST_BUILTIN ;
  86.     stp->stval.bip = p++ ;
  87.   }
  88.   /* seed rand() off the clock */
  89.   { CELL c ;
  90.  
  91.     c.type = 0 ; (void) bi_srand(&c) ;
  92.   }
  93.  
  94.   stp = insert( "length") ;
  95.   stp->type = ST_LENGTH ;
  96. }
  97.  
  98. /**************************************************
  99.  string builtins (except split (in split.c) and [g]sub (at end))
  100.  **************************************************/
  101.  
  102. CELL *bi_length(sp)
  103.   register  CELL *sp ;
  104. { unsigned len ;
  105.  
  106.   if ( sp->type < C_STRING ) cast1_to_s(sp) ;
  107.   len = string(sp)->len ;
  108.  
  109.   free_STRING( string(sp) ) ;
  110.   sp->type = C_DOUBLE ;
  111.   sp->dval = (double) len ;
  112.  
  113.   return sp ;
  114. }
  115.  
  116. char *str_str(target, key , key_len)
  117.   register char *target, *key ;
  118.   unsigned key_len ;
  119.   switch( key_len )
  120.   { case 0 :  return (char *) 0 ;
  121.     case 1 :  return strchr( target, *key) ;
  122.     case 2 :
  123.         while ( target = strchr(target, *key) )
  124.           if ( target[1] == key[1] )  return  target ;
  125.           else target++ ;
  126.         /*failed*/
  127.         return (char *) 0 ;
  128.   }
  129.   key_len-- ;
  130.   while ( target = strchr(target, *key) )
  131.         if ( memcmp(target+1, key+1, key_len) == 0 ) return target ;
  132.         else target++ ;
  133.   /*failed*/
  134.   return (char *) 0 ;
  135. }
  136.  
  137.  
  138.  
  139. CELL *bi_index(sp)
  140.   register CELL *sp ;
  141. { register int idx ;
  142.   unsigned len ;
  143.   char *p ;
  144.  
  145.   sp-- ;
  146.   if ( TEST2(sp) != TWO_STRINGS )
  147.         cast2_to_s(sp) ;
  148.  
  149.   if ( len = string(sp+1)->len )
  150.     idx = (p = str_str(string(sp)->str,string(sp+1)->str,len))
  151.           ? p - string(sp)->str + 1 : 0 ;
  152.  
  153.   else  /* index of the empty string */
  154.     idx = 1 ;
  155.   
  156.   free_STRING( string(sp) ) ;
  157.   free_STRING( string(sp+1) ) ;
  158.   sp->type = C_DOUBLE ;
  159.   sp->dval = (double) idx ;
  160.   return sp ;
  161. }
  162.  
  163. /*  substr(s, i, n)
  164.     if l = length(s)
  165.     then get the characters
  166.     from  max(1,i) to min(l,n-i-1) inclusive */
  167.  
  168. CELL *bi_substr(sp)
  169.   CELL *sp ;
  170. { int n_args, len ;
  171.   register int i, n ;
  172.   char *s ;    /* substr(s, i, n) */
  173.   STRING *sval ;
  174.  
  175.   n_args = sp->type ;
  176.   sp -= n_args ;
  177.   if ( sp->type < C_STRING )  cast1_to_s(sp) ;
  178.   s = (sval = string(sp)) -> str ;
  179.  
  180.   if ( n_args == 2 )  
  181.   { n = 0x7fff  ;  /* essentially infinity */
  182.     if ( sp[1].type != C_DOUBLE ) cast1_to_d(sp+1) ; 
  183.   }
  184.   else
  185.   { if ( sp[1].type + sp[2].type != TWO_STRINGS ) cast2_to_d(sp+1) ;
  186.     n = (int) sp[2].dval ;
  187.   }
  188.   i = (int) sp[1].dval - 1 ; /* i now indexes into string */
  189.  
  190.  
  191.   if ( (len = strlen(s)) == 0 )  return sp ;
  192.   /* get to here is s is not the null string */
  193.   if ( i < 0 ) { n += i ; i = 0 ; }
  194.   if ( n > len - i )  n = len - i ;
  195.  
  196.   if ( n <= 0 )  /* the null string */
  197.   { free_STRING( sval ) ;
  198.     sp->ptr = (PTR) &null_str ;
  199.     null_str.ref_cnt++ ;
  200.   }
  201.   else  /* got something */
  202.   { 
  203.     sp->ptr = (PTR) new_STRING((char *)0, n) ;
  204.     (void) memcpy(string(sp)->str, s+i, n) ;
  205.     string(sp)->str[n] = 0 ;
  206.   }
  207.   return sp ;
  208.  
  209. /*
  210.   match(s,r)
  211.   sp[0] holds s, sp[-1] holds r
  212. */
  213.  
  214. CELL *bi_match(sp)
  215.   register CELL *sp ;
  216. { double d ;
  217.   char *p ;
  218.   unsigned length ;
  219.  
  220.   if ( sp->type != C_RE )  cast_to_RE(sp) ;
  221.   if ( (--sp)->type < C_STRING )  cast1_to_s(sp) ;
  222.  
  223.   if ( p = REmatch(string(sp)->str, (sp+1)->ptr, &length) )
  224.       d = (double) ( p - string(sp)->str + 1 ) ;
  225.   else  d = 0.0 ;
  226.  
  227.   cell_destroy( & bi_vars[RSTART] ) ;
  228.   cell_destroy( & bi_vars[RLENGTH] ) ;
  229.   bi_vars[RSTART].type = C_DOUBLE ;
  230.   bi_vars[RSTART].dval = d ;
  231.   bi_vars[RLENGTH].type = C_DOUBLE ;
  232.   bi_vars[RLENGTH].dval = (double) length ;
  233.  
  234.   free_STRING(string(sp)) ;
  235.     
  236.   sp->type = C_DOUBLE ;  sp->dval = d ;
  237.   return sp ;
  238. }
  239.  
  240.  
  241. /************************************************
  242.   arithemetic builtins
  243.  ************************************************/
  244.  
  245. static void fplib_err( fname, val, error)
  246.   char *fname ;
  247.   double val ;
  248.   char *error ;
  249. {
  250.   rt_error("%s(%g) : %s" , fname, val, error) ;
  251. }
  252.  
  253.  
  254. CELL *bi_sin(sp)
  255.   register CELL *sp ;
  256. #if ! STDC_MATHERR
  257.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  258.   sp->dval = sin( sp->dval ) ;
  259.   return sp ;
  260. #else
  261.   double x ;
  262.  
  263.   errno = 0 ;
  264.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  265.   x = sp->dval ;
  266.   sp->dval = sin( sp->dval ) ;
  267.   if ( errno )  fplib_err("sin", x, "loss of precision") ;
  268.   return sp ;
  269. #endif
  270. }
  271.  
  272. CELL *bi_cos(sp)
  273.   register CELL *sp ;
  274. #if ! STDC_MATHERR
  275.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  276.   sp->dval = cos( sp->dval ) ;
  277.   return sp ;
  278. #else
  279.   double x ;
  280.  
  281.   errno = 0 ;
  282.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  283.   x = sp->dval ;
  284.   sp->dval = cos( sp->dval ) ;
  285.   if ( errno )  fplib_err("cos", x, "loss of precision") ;
  286.   return sp ;
  287. #endif
  288. }
  289.  
  290. CELL *bi_atan2(sp)
  291.   register CELL *sp ;
  292. #if  !  STDC_MATHERR
  293.   sp-- ;
  294.   if ( TEST2(sp) != TWO_DOUBLES ) cast2_to_d(sp) ;
  295.   sp->dval = atan2(sp->dval, (sp+1)->dval) ;
  296.   return sp ;
  297. #else
  298.  
  299.   errno = 0 ;
  300.   sp-- ;
  301.   if ( TEST2(sp) != TWO_DOUBLES ) cast2_to_d(sp) ;
  302.   sp->dval = atan2(sp->dval, (sp+1)->dval) ;
  303.   if ( errno ) rt_error("atan2(0,0) : domain error") ;
  304.   return sp ;
  305. #endif
  306. }
  307.  
  308. CELL *bi_log(sp)
  309.   register CELL *sp ;
  310. #if ! STDC_MATHERR
  311.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  312.   sp->dval = log( sp->dval ) ;
  313.   return sp ;
  314. #else
  315.   double  x ;
  316.  
  317.   errno = 0 ;
  318.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  319.   x = sp->dval ;
  320.   sp->dval = log( sp->dval ) ;
  321.   if ( errno )  fplib_err("log", x, "domain error") ;
  322.   return sp ;
  323. #endif
  324. }
  325.  
  326. CELL *bi_exp(sp)
  327.   register CELL *sp ;
  328. #if  ! STDC_MATHERR
  329.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  330.   sp->dval = exp(sp->dval) ;
  331.   return sp ;
  332. #else
  333.   double  x ;
  334.  
  335.   errno = 0 ;
  336.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  337.   x = sp->dval ;
  338.   sp->dval = exp(sp->dval) ;
  339.   if ( errno && sp->dval)  fplib_err("exp", x, "overflow") ;
  340.      /* on underflow sp->dval==0, ignore */
  341.   return sp ;
  342. #endif
  343. }
  344.  
  345. CELL *bi_int(sp)
  346.   register CELL *sp ;
  347. { if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  348.   sp->dval = sp->dval >= 0.0 ? floor( sp->dval ) : ceil(sp->dval)  ;
  349.   return sp ;
  350. }
  351.  
  352. CELL *bi_sqrt(sp)
  353.   register CELL *sp ;
  354. #if  ! STDC_MATHERR
  355.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  356.   sp->dval = sqrt( sp->dval ) ;
  357.   return sp ;
  358. #else
  359.   double x ;
  360.  
  361.   errno = 0 ;
  362.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  363.   x = sp->dval ;
  364.   sp->dval = sqrt( sp->dval ) ;
  365.   if ( errno )  fplib_err("sqrt", x, "domain error") ;
  366.   return sp ;
  367. #endif
  368. }
  369.  
  370. #ifdef  __TURBOC__
  371. long  biostime(int, long) ;
  372. #define  time(x)  (biostime(0,0L)<<4)
  373. #else
  374. #include <sys/types.h>
  375.  
  376. #if 0
  377. #ifndef  STARDENT
  378. #include <sys/timeb.h>
  379. #endif
  380. #endif
  381.  
  382. #endif
  383.  
  384. CELL *bi_srand(sp)
  385.   register CELL *sp ;
  386. { register long l ; 
  387.   void srand48() ;
  388.  
  389.   if ( sp-- -> type )  /* user seed */
  390.   { if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  391.     l = (long) sp->dval ; }
  392.   else
  393.   { l = (long) time( (time_t *) 0 ) ;
  394.     (++sp)->type = C_DOUBLE ;
  395.     sp->dval = (double) l ;
  396.   }
  397.   srand48(l) ;
  398.   return sp ;
  399. }
  400.     
  401. CELL *bi_rand(sp)
  402.   register CELL *sp ;
  403.  
  404.   (++sp)->type = C_DOUBLE ;
  405.   sp->dval = drand48() ;
  406.   return sp ;
  407. }
  408.  
  409. /*************************************************
  410.  miscellaneous builtins
  411.  close, system and getline
  412.  *************************************************/
  413.  
  414. CELL *bi_close(sp)
  415.   register CELL *sp ;
  416. { int x ;
  417.  
  418.   if ( sp->type < C_STRING ) cast1_to_s(sp) ;
  419.   x = file_close( (STRING *) sp->ptr) ;
  420.   free_STRING( string(sp) ) ;
  421.   sp->type = C_DOUBLE ;
  422.   sp->dval = (double) x ;
  423.   return sp ;
  424. }
  425.  
  426. #if   ! DOS
  427. CELL *bi_system(sp)
  428.   CELL *sp ;
  429. { int pid ;
  430.   unsigned ret_val ;
  431.  
  432.   if ( !shell ) shell = (shell = getenv("SHELL")) ? shell : "/bin/sh" ;
  433.   if ( sp->type < C_STRING ) cast1_to_s(sp) ;
  434.  
  435.   switch( pid = fork() )
  436.   { case -1 :  /* fork failed */
  437.  
  438.        errmsg(errno, "could not create a new process") ;
  439.        ret_val = 128 ;
  440.        break ;
  441.  
  442.     case  0  :  /* the child */
  443.        (void) execl(shell, shell, "-c", string(sp)->str, (char *) 0) ;
  444.        /* if get here, execl() failed */
  445.        errmsg(errno, "execute of %s failed", shell) ;
  446.        fflush(stderr) ;
  447.        _exit(128) ;
  448.  
  449.     default   :  /* wait for the child */
  450.        ret_val = wait_for(pid) ;
  451.        if ( ret_val & 0xff ) ret_val = 128 ;
  452.        else  ret_val = (ret_val & 0xff00) >> 8 ;
  453.        break ;
  454.   }
  455.  
  456.   cell_destroy(sp) ;
  457.   sp->type = C_DOUBLE ;
  458.   sp->dval = (double) ret_val ;
  459.   return sp ;
  460. }
  461.  
  462. #else   /*  DOS   */
  463.  
  464. CELL *bi_system( sp )
  465.   register CELL *sp ;
  466. { rt_error("no system call in MsDos --yet") ;
  467.   return sp ;
  468. }
  469.  
  470. /* prints errmsgs for DOS  */
  471. CELL *bi_errmsg(sp)
  472.   register CELL *sp ;
  473. {
  474.   cast1_to_s(sp) ;
  475.   fprintf(stderr, "%s\n", string(sp)->str) ;
  476.   free_STRING(string(sp)) ;
  477.   sp->type = C_DOUBLE ;
  478.   sp->dval = 0.0 ;
  479.   return sp ;
  480. }
  481. #endif
  482.  
  483.  
  484. /*  getline()  */
  485.  
  486. /*  if type == 0 :  stack is 0 , target address
  487.  
  488.     if type == F_IN : stack is F_IN, expr(filename), target address
  489.  
  490.     if type == PIPE_IN : stack is PIPE_IN, target address, expr(pipename)
  491. */
  492.  
  493. CELL *bi_getline(sp)
  494.   register CELL *sp ;
  495.   CELL tc , *cp ;
  496.   char *p ;
  497.   unsigned len ;
  498.   FIN *fin_p ;
  499.  
  500.  
  501.   switch( sp->type )
  502.   { 
  503.     case 0 :
  504.         sp-- ;
  505.         if ( main_fin == (FIN *) -1 && ! open_main() )
  506.                 goto open_failure ;
  507.     
  508.         if ( ! main_fin || !(p = FINgets(main_fin, &len)) )
  509.                 goto  eof ;
  510.  
  511.         cp = (CELL *) sp->ptr ;
  512.         if ( TEST2(bi_vars+NR) != TWO_DOUBLES ) cast2_to_d(bi_vars+NR) ;
  513.         bi_vars[NR].dval += 1.0 ;
  514.         bi_vars[FNR].dval += 1.0 ;
  515.         break ;
  516.  
  517.     case  F_IN :
  518.         sp-- ;
  519.         if ( sp->type < C_STRING ) cast1_to_s(sp) ;
  520.         fin_p = (FIN *) file_find(sp->ptr, F_IN) ;
  521.         free_STRING(string(sp) ) ;
  522.         sp-- ;
  523.  
  524.         if ( ! fin_p )   goto open_failure ;
  525.         if ( ! (p = FINgets(fin_p, &len)) )  goto eof ; 
  526.         cp = (CELL *) sp->ptr ;
  527.         break ;
  528.  
  529.     case PIPE_IN :
  530.         sp -= 2 ;
  531.         if ( sp->type < C_STRING ) cast1_to_s(sp) ;
  532.         fin_p = (FIN *) file_find(sp->ptr, PIPE_IN) ;
  533.         free_STRING(string(sp)) ;
  534.  
  535.         if ( ! fin_p )   goto open_failure ;
  536.         if ( ! (p = FINgets(fin_p, &len)) )  goto eof ; 
  537.         cp = (CELL *) (sp+1)->ptr ;
  538.         break ;
  539.  
  540.     default : bozo("type in bi_getline") ;
  541.  
  542.   }
  543.  
  544.   /* we've read a line , store it */
  545.  
  546.     if ( len == 0 )
  547.     { tc.type = C_STRING ; 
  548.       tc.ptr = (PTR) &null_str ; 
  549.       null_str.ref_cnt++ ;
  550.     }
  551.     else
  552.     { tc.type = C_MBSTRN ;
  553.       tc.ptr = (PTR) new_STRING((char *) 0, len) ;
  554.       (void) memcpy( string(&tc)->str, p, len) ;
  555.     }
  556.  
  557.     if ( cp  >= field && cp < field+NUM_FIELDS )
  558.            field_assign(cp-field, &tc) ;
  559.     else
  560.     { cell_destroy(cp) ;
  561.       (void) cellcpy(cp, &tc) ;
  562.     }
  563.  
  564.     cell_destroy(&tc) ;
  565.  
  566.   sp->dval = 1.0  ;  goto done ;
  567.  
  568. open_failure :
  569.   sp->dval = -1.0  ; goto done ;
  570.  
  571. eof :
  572.   sp->dval = 0.0  ;  /* fall thru to done  */
  573.  
  574. done :
  575.   sp->type = C_DOUBLE  ;
  576.   return sp ;
  577. }
  578.  
  579. /**********************************************
  580.  sub() and gsub()
  581.  **********************************************/
  582.  
  583. /* entry:  sp[0] = address of CELL to sub on
  584.            sp[-1] = substitution CELL
  585.            sp[-2] = regular expression to match
  586. */
  587.  
  588. CELL *bi_sub( sp )
  589.   register CELL *sp ;
  590. { CELL *cp ; /* pointer to the replacement target */
  591.   CELL tc ;  /* build the new string here */
  592.   CELL sc ;  /* copy of the target CELL */
  593.   char *front, *middle, *back ; /* pieces */
  594.   unsigned front_len, middle_len, back_len ;
  595.  
  596.   sp -= 2 ;
  597.   if ( sp->type != C_RE )  cast_to_RE(sp) ;
  598.   if ( sp[1].type != C_REPL && sp[1].type != C_REPLV )
  599.               cast_to_REPL(sp+1) ;
  600.   cp = (CELL *) (sp+2)->ptr ;
  601.   /* make a copy of the target, because we won't change anything
  602.      including type unless the match works */
  603.   (void) cellcpy(&sc, cp) ;
  604.   if ( sc.type < C_STRING ) cast1_to_s(&sc) ;
  605.   front = string(&sc)->str ;
  606.  
  607.   if ( middle = REmatch(front, sp->ptr, &middle_len) )
  608.   { 
  609.     front_len = middle - front ;
  610.     back = middle + middle_len ; 
  611.     back_len = string(&sc)->len - front_len - middle_len ;
  612.  
  613.     if ( (sp+1)->type == C_REPLV ) 
  614.     { STRING *sval = new_STRING((char *) 0, middle_len) ;
  615.  
  616.       (void) memcpy(sval->str, middle, middle_len) ;
  617.       (void) replv_to_repl(sp+1, sval) ;
  618.       free_STRING(sval) ;
  619.     }
  620.  
  621.     tc.type = C_STRING ;
  622.     tc.ptr = (PTR) new_STRING((char *) 0, 
  623.              front_len + string(sp+1)->len + back_len ) ;
  624.  
  625.     { char *p = string(&tc)->str ;
  626.  
  627.       if ( front_len )
  628.       { (void) memcpy(p, front, front_len) ;
  629.         p += front_len ;
  630.       }
  631.       if ( string(sp+1)->len )
  632.       { (void) memcpy(p, string(sp+1)->str, string(sp+1)->len) ;
  633.         p += string(sp+1)->len ;
  634.       }
  635.       if ( back_len )  (void) memcpy(p, back, back_len) ;
  636.     }
  637.  
  638.     if ( cp  >= field && cp < field+NUM_FIELDS )
  639.            field_assign(cp-field, &tc) ;
  640.     else
  641.     { cell_destroy(cp) ;
  642.       (void) cellcpy(cp, &tc) ;
  643.     }
  644.  
  645.     free_STRING(string(&tc)) ;
  646.   }
  647.  
  648.   free_STRING(string(&sc)) ;
  649.   repl_destroy(sp+1) ;
  650.   sp->type = C_DOUBLE ;
  651.   sp->dval = middle != (char *) 0 ? 1.0 : 0.0 ;
  652.   return sp ;
  653. }
  654.  
  655. static  unsigned repl_cnt ;  /* number of global replacements */
  656.  
  657. /* recursive global subsitution 
  658.    dealing with empty matches makes this mildly painful
  659. */
  660.  
  661. static STRING *gsub( re, repl, target, flag)
  662.   PTR  re ;
  663.   CELL *repl ;  /* always of type REPL or REPLV */
  664.   char *target ;
  665.   int flag ; /* if on, match of empty string at front is OK */
  666. { char *front, *middle ;
  667.   STRING *back ;
  668.   unsigned front_len, middle_len ;
  669.   STRING  *ret_val ;
  670.   CELL xrepl ; /* a copy of repl so we can change repl */
  671.  
  672.   if ( ! (middle = REmatch(target, re, &middle_len)) )
  673.       return  new_STRING(target) ; /* no match */
  674.  
  675.   (void) cellcpy(&xrepl, repl) ;
  676.  
  677.   if ( !flag && middle_len == 0 && middle == target ) 
  678.   { /* match at front that's not allowed */
  679.  
  680.     if ( *target == 0 )  /* target is empty string */
  681.     { null_str.ref_cnt++ ;
  682.       return & null_str ;
  683.     }
  684.     else
  685.     { char xbuff[2] ;
  686.  
  687.       front_len = 0 ;
  688.       /* make new repl with target[0] */
  689.       repl_destroy(repl) ;
  690.       xbuff[0] = *target++ ;  xbuff[1] = 0 ;
  691.       repl->type = C_REPL ;
  692.       repl->ptr = (PTR) new_STRING( xbuff ) ;
  693.       back = gsub(re, &xrepl, target, 1) ;
  694.     }
  695.   }
  696.   else  /* a match that counts */
  697.   { repl_cnt++ ;
  698.  
  699.     front = target ;
  700.     front_len = middle - target ;
  701.  
  702.     if ( *middle == 0 )  /* matched back of target */
  703.     { back = &null_str ; null_str.ref_cnt++ ; }
  704.     else back = gsub(re, &xrepl, middle + middle_len, 0) ;
  705.       
  706.     /* patch the &'s if needed */
  707.     if ( repl->type == C_REPLV )
  708.     { STRING *sval = new_STRING((char *) 0, middle_len) ;
  709.  
  710.       (void) memcpy(sval->str, middle, middle_len) ;
  711.       (void) replv_to_repl(repl, sval) ;
  712.       free_STRING(sval) ;
  713.     }
  714.   }
  715.  
  716.   /* put the three pieces together */
  717.   ret_val = new_STRING((char *)0,
  718.               front_len + string(repl)->len + back->len); 
  719.   { char *p = ret_val->str ;
  720.  
  721.     if ( front_len )
  722.     { (void) memcpy(p, front, front_len) ; p += front_len ; }
  723.     if ( string(repl)->len )
  724.     { (void) memcpy(p, string(repl)->str, string(repl)->len) ;
  725.       p += string(repl)->len ;
  726.     }
  727.     if ( back->len ) (void) memcpy(p, back->str, back->len) ;
  728.   }
  729.  
  730.   /* cleanup, repl is freed by the caller */
  731.   repl_destroy(&xrepl) ;
  732.   free_STRING(back) ;
  733.  
  734.   return ret_val ;
  735. }
  736.  
  737. /* set up for call to gsub() */
  738. CELL *bi_gsub( sp )
  739.   register CELL *sp ;
  740. { CELL *cp ;  /* pts at the replacement target */
  741.   CELL sc  ;  /* copy of replacement target */
  742.   CELL tc  ;  /* build the result here */
  743.  
  744.   sp -= 2 ;
  745.   if ( sp->type != C_RE ) cast_to_RE(sp) ;
  746.   if ( (sp+1)->type != C_REPL && (sp+1)->type != C_REPLV )
  747.           cast_to_REPL(sp+1) ;
  748.  
  749.   (void) cellcpy(&sc, cp = (CELL *)(sp+2)->ptr) ;
  750.   if ( sc.type < C_STRING ) cast1_to_s(&sc) ;
  751.  
  752.   repl_cnt = 0 ;
  753.   tc.ptr = (PTR) gsub(sp->ptr, sp+1, string(&sc)->str, 1) ;
  754.  
  755.   if ( repl_cnt )
  756.   { tc.type = C_STRING ;
  757.  
  758.     if ( cp >= field && cp < field + NUM_FIELDS )
  759.         field_assign(cp-field, &tc) ;
  760.     else
  761.     { cell_destroy(cp) ; (void) cellcpy(cp, &tc) ; }
  762.   }
  763.  
  764.   /* cleanup */
  765.   free_STRING(string(&sc)) ; free_STRING(string(&tc)) ;
  766.   repl_destroy(sp+1) ;
  767.  
  768.   sp->type = C_DOUBLE ;
  769.   sp->dval = (double) repl_cnt ;
  770.   return sp ;
  771. }
  772.